#include "GameObject.h"
#include "Animation.h"
#include "AnimDescriptor.h"
#include "Transformation.h"
#include "GLTexture2D.h"
#include "TextureManager.h"
#include "corona.h"
#include "Logger.h"
#include "Exception.h"

using namespace game_engine;
using namespace game_engine::components;
using namespace game_engine::data_format;
using namespace game_engine::utils;
using namespace game_engine::open_gl;
using namespace corona;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

Animation::Animation(void)
    : Component(), descFile(), imageFile(), anchor(), frameSize(), 
    sequenceTime(), currentFrame(), activeSequence()
{
    type = "Animation";
    sequences.clear();
}

Animation::Animation(GameObject *obj, const string &desc, const string &img)
    : Component(obj), descFile(desc), imageFile(img), anchor(), frameSize(),
    sequenceTime(), currentFrame(), activeSequence()
{
    type = "Animation";
    sequences.clear();
}

Animation::~Animation(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

const BOOL Animation::sequenceExists(const string &sequence) const
{
    map<string, AnimationSequence>::const_iterator it;
    it = sequences.find(sequence);
    return (it != sequences.end());
}

void Animation::resetSequence(void)
{
    sequenceTime = 0.0f;
    currentFrame = 0;
}

void Animation::initialize(void) throw(...)
{
    if (descFile.empty() || imageFile.empty())
    {
        string err("Animation component not properly initialized");
        Logger::ERR(GAMEENGINE_LOG, err);
        throw RuntimeException(err);
    }

    // Parse the descriptor file that contains all the information about the 
    // animation sequences
    AnimDescriptor descriptor(descFile);
    try
    {
        descriptor.parse(imageFile);
    } catch (exception &e)
    {
        Logger::ERR(GAMEENGINE_LOG, "error parsing " + descFile + ": " + 
            e.what());
        throw;
    }

    anchor = descriptor.getAnchor();
    frameSize = descriptor.getFrameSize();

    // Open image with Corona
    Image* img = corona::OpenImage(imageFile.c_str());
    if (img == NULL)
    {
        string err("error opening ");
        err.append(imageFile);
        Logger::ERR(GAMEENGINE_LOG, err);
        throw RuntimeException(err);
    }
	
    // Get each of the animation sequences 
    vector<SequenceInfo> seq = descriptor.getSequences(imageFile);
    vector<SequenceInfo>::iterator it;
    for (it=seq.begin() ;it!=seq.end(); it++)
    {
        SequenceInfo seqInfo = (*it);
        AnimationSequence s(frameSize.getWidth(), frameSize.getHeight(),
            seqInfo.getFrames(), img->getWidth());
        sequences[seqInfo.getName()] = s;
    }

    // Load the texture file
    TextureManager *tm = TextureManager::getInstance();
    tm->setTexture(imageFile, GL_RGBA);    
}

void Animation::update(const FLOAT32 time)
{
    AnimationSequence seq = getSequence(activeSequence);

    // FIXME : verify this conversion!!!
    // Convert time to milliseconds
    //sequenceTime += (time * 100.0f);
    sequenceTime += (REFRESH_TIME * 100.0f);
    if (sequenceTime > (seq.getTimeout(seq.getFrameCount() - 1)))
    {
        sequenceTime -= (seq.getTimeout(seq.getFrameCount() - 1));
    }

    for(INT32 i=0; i<seq.getFrameCount(); i++)
    {
        if (sequenceTime < (seq.getTimeout(i)))
        {
            currentFrame = i;
            break;
        }
    }
}

void Animation::render(void) throw(...)
{
    // Retrive the Transformation component
    Transformation *transf;
    try
    {
        transf = dynamic_cast<Transformation*> 
            (object->getComponent("Transformation"));
    } catch (...)
    {
        string msg("[PlainTexture::render] transformation component not found");
        throw RuntimeException(msg);
    }

    // Calculate offset according to camera position
    Point offset = computeCameraOffset();
    Point newPosition(offset.getX() + transf->getXPosition(),
        offset.getY() + transf->getYPosition());

    AnimationSequence seq = sequences.find(activeSequence)->second;
    Rect currFrame = seq.getClipRegion(currentFrame);
    FrameInfo f = seq.getFrameInfo(currentFrame);

    // Draw texture at object's position
    TextureManager *tm = TextureManager::getInstance();
    GLTexture2D texture = tm->getTexture(imageFile);
    Rect region(newPosition.getX() + (f.getFlipHorizontally()?currFrame.getWidth():0) - anchor.getX(),
        newPosition.getY() - anchor.getY(),
        currFrame.getHeight(),
        f.getFlipHorizontally()?-currFrame.getWidth():currFrame.getWidth());

    texture.drawInRect(region, currFrame, f.getRotation(), f.getScale());
}

//void Animation::drawAtPoint(const Point &point, const string &sequence, 
//    const INT32 frame) const
//{
//    AnimationSequence seq = sequences.find(sequence)->second;
//    Rect currFrame = seq.getClipRegion(frame);
//    FrameInfo f = seq.getFrameInfo(frame);
//
//    TextureManager *tm = TextureManager::getInstance();
//    GLTexture2D tex = tm->getTexture(imageFile);
//    Rect region(point.getX() + (f.getFlipHorizontally()?currFrame.getWidth():0) - anchor.getX(),
//        point.getY() - anchor.getY(),
//        currFrame.getHeight(),
//        f.getFlipHorizontally()?-currFrame.getWidth():currFrame.getWidth());
//
//    tex.drawInRect(region, currFrame, f.getRotation(), f.getScale());
//}

///////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////

const Point& Animation::getAnchor(void) const
{
    return anchor;
}

const Size& Animation::getFrameSize(void) const
{
    return frameSize;
}

const AnimationSequence& Animation::getSequence(const string &sequence)
{
    return sequences.find(sequence)->second;
}

const string& Animation::getFirstSequenceName(void) const
{
    map<string, AnimationSequence>::const_iterator it(sequences.begin());
    return it->first;
}

const string& Animation::getActiveSequence(void) const
{
    return activeSequence;
}

///////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////

void Animation::setDescFile(const string &file)
{
    descFile = file;
}

void Animation::setImageFile(const string &file)
{
    imageFile = file;
}

void Animation::setActiveSequence(const string &file)
{
    activeSequence = file;
}
